﻿using System;
using System.Collections.Generic;
using System.Linq;
using BouncingBallGame.Models;
using Newtonsoft.Json;
using BouncingBallGame.Helpers;

namespace BouncingBallGame.DataModel
{
    public class LevelsManager
    {
        public const string LevelPrefix = "Level-";
        public const string SaveGamePrefix = "Save-";
        private static readonly List<string> defaultLevels = new List<string>()
        {
            @"{""Score"":0.0,""Name"":""Big hole in the middle!"",""Width"":600.0,""Height"":300.0,""GravityAcceleration"":1000.0,""Ball"":{""Radius"":20.0,""X"":20.0,""Y"":280.0,""NormalCollisionSpeedLoss"":0.65,""TangentCollisionSpeedLoss"":0.8},""Obstacles"":[{""Width"":30.0,""Height"":200.0,""Rotation"":30.0,""X"":200.0,""Y"":200.0},{""Width"":30.0,""Height"":200.0,""Rotation"":200.0,""X"":300.0,""Y"":100.0}],""Gifts"":[{""Height"":10.0,""Width"":10.0,""X"":200.0,""Y"":50.0,""Rotation"":0.0},{""Height"":20.0,""Width"":30.0,""X"":400.0,""Y"":100.0,""Rotation"":0.0},{""Height"":10.714285714285715,""Width"":22.857142857142858,""X"":352.14285714285717,""Y"":262.85714285714289,""Rotation"":0.0},{""Height"":9.2857142857142865,""Width"":4.2857142857142856,""X"":527.14285714285711,""Y"":226.42857142857145,""Rotation"":0.0},{""Height"":12.857142857142858,""Width"":5.0,""X"":535.71428571428578,""Y"":30.714285714285715,""Rotation"":0.0},{""Height"":4.2857142857142856,""Width"":6.4285714285714288,""X"":391.42857142857144,""Y"":34.285714285714285,""Rotation"":0.0},{""Height"":11.428571428571429,""Width"":7.1428571428571432,""X"":252.14285714285714,""Y"":255.0,""Rotation"":0.0},{""Height"":7.8571428571428576,""Width"":11.428571428571429,""X"":25.0,""Y"":21.428571428571431,""Rotation"":0.0},{""Height"":10.714285714285715,""Width"":13.571428571428571,""X"":46.428571428571431,""Y"":143.57142857142858,""Rotation"":0.0}]}",
            @"{""Score"":0.0,""Name"":""Heavy ball"",""Width"":600.0,""Height"":300.0,""GravityAcceleration"":6428.5714285714284,""Ball"":{""Radius"":20.681491494178768,""X"":24.245217442512512,""Y"":274.99910128968105,""NormalCollisionSpeedLoss"":0.8,""TangentCollisionSpeedLoss"":0.8},""Obstacles"":[{""Width"":78.773430111587032,""Height"":21.334470655221441,""Rotation"":0.5625135778801913,""X"":115.03036997147969,""Y"":257.89455264806747},{""Width"":121.42857142857143,""Height"":21.428571428571431,""Rotation"":0.0,""X"":208.57367698635375,""Y"":187.83798779760087},{""Width"":109.30163712077122,""Height"":19.887375745976662,""Rotation"":0.061887609139900254,""X"":307.85039378064022,""Y"":114.2938650080136},{""Width"":27.713640628904759,""Height"":25.592306367069352,""Rotation"":-0.50676774880443265,""X"":392.13835741792406,""Y"":225.69829336234503},{""Width"":32.142857142857146,""Height"":18.571428571428573,""Rotation"":0.0,""X"":485.0,""Y"":203.57142857142858}],""Gifts"":[{""Height"":13.571428571428571,""Width"":25.0,""X"":112.14285714285713,""Y"":72.857142857142861,""Rotation"":0.0},{""Height"":37.857142857142861,""Width"":37.857142857142861,""X"":298.57142857142856,""Y"":54.285714285714285,""Rotation"":0.0},{""Height"":16.428571428571431,""Width"":17.142857142857142,""X"":394.28571428571428,""Y"":158.57142857142858,""Rotation"":0.0},{""Height"":14.273347130843572,""Width"":18.565804958343506,""X"":312.14510632412771,""Y"":244.26882198878698,""Rotation"":0.0},{""Height"":17.284744619142856,""Width"":25.406765536043231,""X"":241.42857142857145,""Y"":238.57142857142858,""Rotation"":38.183055645891642},{""Height"":25.0,""Width"":28.571428571428573,""X"":97.142857142857125,""Y"":169.99999999999997,""Rotation"":0.0},{""Height"":7.1428571428571432,""Width"":8.5714285714285712,""X"":573.57142857142856,""Y"":13.571428571428571,""Rotation"":0.0}]}",
            @"{""Score"":0.0,""Name"":""Ramping"",""Width"":600.0,""Height"":300.0,""GravityAcceleration"":1000.0,""Ball"":{""Radius"":29.286864612783706,""X"":32.850393227168496,""Y"":267.14949075664794,""NormalCollisionSpeedLoss"":0.65,""TangentCollisionSpeedLoss"":0.8},""Obstacles"":[{""Width"":30.0,""Height"":200.0,""Rotation"":30.0,""X"":233.58380287885663,""Y"":185.71865694863456},{""Width"":30.0,""Height"":200.0,""Rotation"":200.0,""X"":300.0,""Y"":100.0},{""Width"":204.28571428571425,""Height"":30.0,""Rotation"":0.0,""X"":233.57705116271973,""Y"":184.99652939183372}],""Gifts"":[{""Height"":10.0,""Width"":10.0,""X"":200.0,""Y"":50.0,""Rotation"":0.0},{""Height"":20.0,""Width"":30.0,""X"":400.0,""Y"":100.0,""Rotation"":0.0},{""Height"":27.142857142857142,""Width"":35.0,""X"":435.71428571428572,""Y"":233.57142857142858,""Rotation"":0.0},{""Height"":15.0,""Width"":27.142857142857142,""X"":379.28571428571428,""Y"":253.57142857142858,""Rotation"":0.0},{""Height"":4.2857142857142856,""Width"":5.0,""X"":254.28571428571425,""Y"":221.42857142857145,""Rotation"":0.0},{""Height"":8.5714285714285712,""Width"":5.7142857142857144,""X"":20.0,""Y"":10.714285714285715,""Rotation"":0.0},{""Height"":10.0,""Width"":10.0,""X"":275.71428571428572,""Y"":10.0,""Rotation"":0.0},{""Height"":5.7142857142857144,""Width"":9.2857142857142865,""X"":567.14285714285711,""Y"":17.857142857142858,""Rotation"":0.0}]}",
            @"{""Score"":0.0,""Name"":""Small hole in the middle!"",""Width"":600.0,""Height"":300.0,""GravityAcceleration"":1000.0,""Ball"":{""Radius"":50.0,""X"":50.0,""Y"":250.0,""NormalCollisionSpeedLoss"":0.65,""TangentCollisionSpeedLoss"":0.8},""Obstacles"":[{""Width"":30.0,""Height"":200.0,""Rotation"":30.0,""X"":200.0,""Y"":200.0},{""Width"":30.0,""Height"":200.0,""Rotation"":200.0,""X"":300.0,""Y"":100.0}],""Gifts"":[{""Height"":10.0,""Width"":10.0,""X"":200.0,""Y"":50.0,""Rotation"":0.0},{""Height"":20.0,""Width"":30.0,""X"":400.0,""Y"":100.0,""Rotation"":0.0},{""Height"":7.1428571428571432,""Width"":7.1428571428571432,""X"":60.0,""Y"":42.142857142857146,""Rotation"":0.0},{""Height"":7.8571428571428576,""Width"":7.1428571428571432,""X"":538.57142857142856,""Y"":270.71428571428572,""Rotation"":0.0},{""Height"":16.428571428571431,""Width"":9.2857142857142865,""X"":535.0,""Y"":63.571428571428569,""Rotation"":0.0},{""Height"":11.428571428571429,""Width"":12.857142857142858,""X"":323.57142857142856,""Y"":158.57142857142858,""Rotation"":0.0},{""Height"":18.571428571428573,""Width"":17.857142857142858,""X"":280.71428571428572,""Y"":245.0,""Rotation"":0.0},{""Height"":5.7142857142857144,""Width"":6.4285714285714288,""X"":427.14285714285717,""Y"":246.42857142857145,""Rotation"":0.0},{""Height"":17.857142857142858,""Width"":13.571428571428571,""X"":534.28571428571433,""Y"":153.57142857142858,""Rotation"":0.0},{""Height"":6.4285714285714288,""Width"":5.7142857142857144,""X"":357.85714285714289,""Y"":20.0,""Rotation"":0.0}]}",
            @"{""Score"":0.0,""Name"":""Stair"",""Width"":600.0,""Height"":300.0,""GravityAcceleration"":1428.5714285714287,""Ball"":{""Radius"":20.681491494178768,""X"":24.245217570236751,""Y"":275.72801636798044,""NormalCollisionSpeedLoss"":0.8,""TangentCollisionSpeedLoss"":0.8},""Obstacles"":[{""Width"":85.477751694325732,""Height"":23.150224417213195,""Rotation"":-90.6779374804433,""X"":188.56917904956,""Y"":300.73858180216382},{""Width"":121.42857142857143,""Height"":21.428571428571431,""Rotation"":0.0,""X"":187.84027010202408,""Y"":198.55574429035187},{""Width"":116.40720150300435,""Height"":25.728761511189592,""Rotation"":0.0,""X"":286.44206975187575,""Y"":107.13969447783062},{""Width"":25.004500278404777,""Height"":112.14285714285713,""Rotation"":0.0,""X"":285.03262132406229,""Y"":107.15869482074466}],""Gifts"":[{""Height"":13.571428571428571,""Width"":25.0,""X"":119.26996605736866,""Y"":82.144065414156231,""Rotation"":0.0},{""Height"":37.857142857142861,""Width"":37.857142857142861,""X"":239.28684026002881,""Y"":153.55319883142198,""Rotation"":0.0},{""Height"":16.428571428571431,""Width"":17.142857142857142,""X"":376.44094526767731,""Y"":150.7153401204518,""Rotation"":0.0},{""Height"":29.285714285714281,""Width"":30.714285714285715,""X"":490.0,""Y"":199.28571428571425,""Rotation"":0.0},{""Height"":11.428571428571429,""Width"":24.285714285714281,""X"":262.85713825907027,""Y"":255.708706336362,""Rotation"":0.0},{""Height"":8.5714285714285712,""Width"":7.1428571428571432,""X"":557.85714285714289,""Y"":25.0,""Rotation"":0.0}]}",
            @"{""Score"":0.0,""Name"":""Stairs"",""Width"":600.0,""Height"":300.0,""GravityAcceleration"":1000.0,""Ball"":{""Radius"":20.681491494178768,""X"":24.245217442512512,""Y"":274.99910128968105,""NormalCollisionSpeedLoss"":0.8,""TangentCollisionSpeedLoss"":0.8},""Obstacles"":[{""Width"":85.477751694325732,""Height"":23.150224417213195,""Rotation"":-90.6779374804433,""X"":188.56917904956,""Y"":300.73858180216382},{""Width"":121.42857142857143,""Height"":21.428571428571431,""Rotation"":0.0,""X"":187.13835780109679,""Y"":195.69407633372717},{""Width"":141.40607540096556,""Height"":25.728761511189592,""Rotation"":0.0,""X"":306.4195728302002,""Y"":98.5816878080368}],""Gifts"":[{""Height"":13.571428571428571,""Width"":25.0,""X"":112.14285714285713,""Y"":72.857142857142861,""Rotation"":0.0},{""Height"":37.857142857142861,""Width"":37.857142857142861,""X"":298.57142857142856,""Y"":54.285714285714285,""Rotation"":0.0},{""Height"":16.428571428571431,""Width"":17.142857142857142,""X"":394.28571428571428,""Y"":158.57142857142858,""Rotation"":0.0},{""Height"":10.027728592410298,""Width"":10.027728592410298,""X"":512.14285714285711,""Y"":260.0,""Rotation"":-265.91533171433082},{""Height"":10.024203556363748,""Width"":9.5821058750195078,""X"":545.0,""Y"":25.0,""Rotation"":-4.37128597349569},{""Height"":7.1428571428571432,""Width"":8.5714285714285712,""X"":222.85714285714286,""Y"":228.57142857142858,""Rotation"":0.0},{""Height"":7.8571428571428576,""Width"":11.428571428571429,""X"":547.14285714285711,""Y"":161.42857142857145,""Rotation"":0.0}]}",
        };

        public List<GameLevelModel> Levels { get; set; }

        public List<GameLevelModel> DefaultLevels { get; set; }

        static LevelsManager()
        {

        }

        public LevelsManager()
        {
            this.Levels = new List<GameLevelModel>(GetAllRoamingLevels());

            if (Levels.Count == 0)
            {
                this.LoadDefaultLevels();
            }
        }

        private void LoadDefaultLevels()
        {
            foreach (string level in LevelsManager.defaultLevels)
            {
                GameLevelModel gameLevel;

                if (LevelsManager.TryGetFromJson(level, out gameLevel))
                {
                    this.Levels.Add(gameLevel);
                    LevelsManager.AddLevelToRoaming(gameLevel);
                }
            }
        }

        public static GameLevelModel Empty
        {
            get
            {
                return new GameLevelModel()
                {
                    Name = "",
                    Width = 600,
                    Height = 300,
                    Ball = new BallModel()
                    {
                        Radius = 50,
                        X = 50,
                        Y = 250,
                        NormalCollisionSpeedLoss = 0.65,
                        TangentCollisionSpeedLoss = 0.8,
                    },
                    GravityAcceleration = 10 * 50 / 0.5,
                    Obstacles = new ObstacleModel[]
                    {
                       
                    },
                    Gifts = new GiftModel[]
                    {
                        
                    }
                };
            }
        }

        public static GameLevelModel Level1
        {
            get
            {
                return new GameLevelModel()
                {
                    Name = "Small hole in the middle!",
                    Width = 600,
                    Height = 300,
                    Ball = new BallModel()
                    {
                        Radius = 50,
                        X = 50,
                        Y = 250,
                        NormalCollisionSpeedLoss = 0.65,
                        TangentCollisionSpeedLoss = 0.8,
                    },
                    GravityAcceleration = 10 * 50 / 0.5,
                    Obstacles = new ObstacleModel[]
                    {
                        new ObstacleModel()
                        {
                             Height = 200,
                             Width = 30,
                             X = 200,
                             Y = 200,
                             Rotation = 30,
                        },
                        new ObstacleModel()
                        {
                             Height = 200,
                             Width = 30,
                             X = 300,
                             Y = 100,
                             Rotation = 200,
                        },
                    },
                    Gifts = new GiftModel[]
                    {
                        new GiftModel()
                        {
                            Height = 10,
                            Width = 10,
                            X = 200,
                            Y = 50,
                        },
                        new GiftModel()
                        {
                            Height = 20,
                            Width = 30,
                            X = 400,
                            Y = 100,
                        },
                    }
                };
            }
        }

        public static GameLevelModel Level2
        {
            get
            {
                return new GameLevelModel()
                {
                    Name = "Big hole in the middle!",
                    Width = 600,
                    Height = 300,
                    Score = 0,
                    Ball = new BallModel()
                    {
                        Radius = 20,
                        X = 20,
                        Y = 280,
                        NormalCollisionSpeedLoss = 0.65,
                        TangentCollisionSpeedLoss = 0.8,
                    },
                    GravityAcceleration = 10 * 50 / 0.5,
                    Obstacles = new ObstacleModel[]
                    {
                        new ObstacleModel()
                        {
                             Height = 200,
                             Width = 30,
                             X = 200,
                             Y = 200,
                             Rotation = 30,
                        },
                        new ObstacleModel()
                        {
                             Height = 200,
                             Width = 30,
                             X = 300,
                             Y = 100,
                             Rotation = 200,
                        },
                    },
                    Gifts = new GiftModel[]
                    {
                        new GiftModel()
                        {
                            Height = 10,
                            Width = 10,
                            X = 200,
                            Y = 50,
                        },
                        new GiftModel()
                        {
                            Height = 20,
                            Width = 30,
                            X = 400,
                            Y = 100,
                        },
                    }
                };
            }
        }

        public static bool TryGetFromJson(string json, out GameLevelModel gameLevel)
        {
            gameLevel = null;

            try
            {
                gameLevel = JsonConvert.DeserializeObject<GameLevelModel>(json);
            }
            catch
            {
                return false;
            }

            return true;
        }

        public static string ToJson(GameLevelModel gameLevel)
        {
            return JsonConvert.SerializeObject(gameLevel);
        }

        public static string LevelRoamingName(GameLevelModel gameLevel)
        {
            return String.Format("{0}{1}", LevelPrefix, gameLevel.Name);
        }

        public static string LevelRoamingName(string name)
        {
            return String.Format("{0}{1}", LevelPrefix, name);
        }

        public static string SaveGameRoamingName(GameLevelModel gameLevel)
        {
            return String.Format("{0}{1}", SaveGamePrefix, gameLevel.Name);
        }

        public static string SaveGameRoamingName(string name)
        {
            return String.Format("{0}{1}", SaveGamePrefix, name);
        }

        public static IEnumerable<GameLevelModel> GetAllRoamingLevels()
        {
            foreach (var keyValue in Windows.Storage.ApplicationData.Current.RoamingSettings.Values)
            {
                GameLevelModel gameLevel;
                string json = keyValue.Value as String;
                if (!string.IsNullOrEmpty(json) && TryGetFromJson(json, out gameLevel) && !keyValue.Key.StartsWith(SaveGamePrefix))
                {
                    yield return gameLevel;
                }
            }
        }

        public static void AddLevelToRoaming(GameLevelModel gameLevel)
        {
            var roaming = Windows.Storage.ApplicationData.Current.RoamingSettings;
            roaming.Values[LevelRoamingName(gameLevel)] = ToJson(gameLevel);
        }

        public static void RemoveLevelFromRoaming(string name)
        {
            var roaming = Windows.Storage.ApplicationData.Current.RoamingSettings;
            roaming.Values.Remove(LevelRoamingName(name));
        }

        public static bool TryGetLevelFromRoaming(string name, out GameLevelModel gameLevel)
        {
            gameLevel = null;
            var roaming = Windows.Storage.ApplicationData.Current.RoamingSettings;
            string json = roaming.Values[LevelRoamingName(name)] as String;
            if (string.IsNullOrEmpty(json))
            {
                return false;
            }

            return TryGetFromJson(json, out gameLevel);
        }

        public static void AddSaveGameToRoaming(GameLevelModel gameLevel)
        {
            var roaming = Windows.Storage.ApplicationData.Current.RoamingSettings;
            roaming.Values[SaveGameRoamingName(gameLevel)] = ToJson(gameLevel);
        }

        public static void RemoveSaveGameFromRoaming(string name)
        {
            var roaming = Windows.Storage.ApplicationData.Current.RoamingSettings;
            roaming.Values.Remove(SaveGameRoamingName(name));
        }

        public static bool TryGetSaveGameFromRoaming(string name, out GameLevelModel gameLevel)
        {
            gameLevel = null;
            var roaming = Windows.Storage.ApplicationData.Current.RoamingSettings;
            string json = roaming.Values[SaveGameRoamingName(name)] as String;
            if (string.IsNullOrEmpty(json))
            {
                return false;
            }

            return TryGetFromJson(json, out gameLevel);
        }

        public static bool DoesLevelRoamingExists(string name)
        {
            var roaming = Windows.Storage.ApplicationData.Current.RoamingSettings;

            if (string.IsNullOrEmpty(roaming.Values[LevelRoamingName(name)] as String))
            {
                return false;
            }

            return true;
        }

        public static bool DoesSaveGameRoamingExists(string name)
        {
            var roaming = Windows.Storage.ApplicationData.Current.RoamingSettings;

            if (string.IsNullOrEmpty(roaming.Values[SaveGameRoamingName(name)] as String))
            {
                return false;
            }

            return true;
        }
    }
}
